/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-01-26     armink       the first version
 */

#include <fal.h>

#include <at32f435_437_flash.h>

#define AT32_FLASH_SECTOR_SIZE (4096)
#define AT32_FLASH_SECTOR_NUM (1007)

#define AT32_FLASH_START_ADDR (at32f437_onchip_flash.addr)

/**
 * Get the sector of a given address
 *
 * @param address flash address
 *
 * @return The sector of a given address
 */
static uint32_t at32_get_sector_addr(uint32_t address)
{
    uint32_t sector = 0;
    uint32_t sector_addr = 0;

    sector = address / AT32_FLASH_SECTOR_SIZE;
    sector_addr = sector * AT32_FLASH_SECTOR_SIZE + AT32_FLASH_START_ADDR;

    return sector_addr;
}

static int init(void)
{
    /* do nothing now */
}

static int read(long offset, uint8_t *buf, size_t size)
{
    size_t i;
    uint32_t addr = at32f437_onchip_flash.addr + offset;
    for (i = 0; i < size; i++, addr++, buf++)
    {
        *buf = *(uint8_t *)addr;
    }

    return size;
}

static int write(long offset, const uint8_t *buf, size_t size)
{
    size_t i;
    uint32_t read_data;
    uint32_t addr = at32f437_onchip_flash.addr + offset;

    flash_unlock();
    // FLASH_ClearFlag(
    //         FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR
    //                 | FLASH_FLAG_PGSERR);

    // flash_flag_clear(FLASH_BANK1_OBF_FLAG | FLASH_BANK1_ODF_FLAG |
    //                  FLASH_BANK1_PRGMERR_FLAG | FLASH_BANK1_EPPERR_FLAG |
    //                  FLASH_BANK2_OBF_FLAG | FLASH_BANK2_ODF_FLAG |
    //                  FLASH_BANK2_PRGMERR_FLAG | FLASH_BANK2_EPPERR_FLAG);

    for (i = 0; i < size; i++, buf++, addr++)
    {
        /* write data */
        flash_byte_program(addr, *buf);
        read_data = *(__O uint8_t *)addr;
        /* check data */
        if (read_data != *buf)
        {
            flash_lock();
            return -1;
        }
    }
    flash_lock();
    return size;
}

static int erase(long offset, size_t size)
{
    flash_status_type flash_status;
    size_t erased_size = 0;
    uint32_t cur_erase_sector;
    uint32_t end_addr = offset + size;
    // uint32_t addr = at32f437_onchip_flash.addr + offset;

    /* start erase */
    flash_unlock();
    // FLASH_ClearFlag(
    //         FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR
    //                 | FLASH_FLAG_PGSERR);

    // flash_flag_clear(FLASH_BANK1_OBF_FLAG | FLASH_BANK1_ODF_FLAG |
    //                  FLASH_BANK1_PRGMERR_FLAG | FLASH_BANK1_EPPERR_FLAG |
    //                  FLASH_BANK2_OBF_FLAG | FLASH_BANK2_ODF_FLAG |
    //                  FLASH_BANK2_PRGMERR_FLAG | FLASH_BANK2_EPPERR_FLAG);

    /* it will stop when erased size is greater than setting size */
    while (erased_size < size)
    {
        cur_erase_sector = at32_get_sector_addr(offset + erased_size);

        flash_status = flash_sector_erase(cur_erase_sector);
        if (flash_status != FLASH_OPERATE_DONE)
        {
            flash_lock();
            return -1;
        }
        erased_size += AT32_FLASH_SECTOR_SIZE;
    }
    flash_lock();

    return size;
}

const struct fal_flash_dev at32f437_onchip_flash =
    {
        .name = "at32_onchip",
        .addr = 0x08000000,
        .len = 4 * 1024 * 1024 - 64 * 1024,
        .blk_size = 4 * 1024,
        .ops = {init, read, write, erase},
        .write_gran = 8};

// static int fal_flash_at32_init(void)
// {
//     // fal_init();
// }
// INIT_DEVICE_EXPORT(fal_flash_at32_init);